package ru.spb.osll.myfit.client.manager;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.rpc.AsyncCallback;
import ru.spb.osll.myfit.client.service.PageExecutingService;
import ru.spb.osll.myfit.client.service.PageExecutingServiceAsync;
import ru.spb.osll.myfit.client.widget.ExecutingListener;
import ru.spb.osll.myfit.client.widget.PageExecutingListener;
import ru.spb.osll.myfit.common.ExecutingResult;
import ru.spb.osll.myfit.common.PageExecutingResult;
import ru.spb.osll.myfit.common.TestResultMetadataConstants;

import java.util.*;

public class PageExecutingManager {

  private static final int TIMER_INTERVAL = 500;

  private final PageExecutingServiceAsync m_pageExecutingService = GWT
          .create(PageExecutingService.class);

  private HashMap<String, List<ExecutingListener>> m_listeners = new HashMap<String, List<ExecutingListener>>();

  private final Set<String> m_resultsToListen = new HashSet<String>();

  private Timer m_timer = new Timer() {
    @Override
    public void run() {
      synchronized (m_resultsToListen) {      // synchronized has no effect in GWT cos javascript is single-threaded
        processExecutingPages(m_resultsToListen);
      }
    }
  };

  public void executeTestPage(final List<PageExecutingListener> listeners,
                              String pagePath, String comment) {
    TreeMap<String, String> metadata = new TreeMap<String, String>();
    metadata.put(TestResultMetadataConstants.TEST_RUN_COMMENT, comment);
    m_pageExecutingService.executePage(pagePath, metadata,
            new AsyncCallback<PageExecutingResult>() {

              @Override
              public void onSuccess(PageExecutingResult pageExecutingResult) {
                for (PageExecutingListener listener : listeners) {
                  listener.processPageExecutingResult(pageExecutingResult);
                }
              }

              @Override
              public void onFailure(Throwable arg0) {

              }
            });
  }

  public void addResultToListen(final ExecutingResult result) {
    if (result != null) {
      String id = result.getId();
      synchronized (m_resultsToListen) {     // synchronized has no effect in GWT cos javascript is single-threaded
        if (m_resultsToListen.size() == 0) {
          m_timer.scheduleRepeating(TIMER_INTERVAL);
        }
        m_resultsToListen.add(id);
      }
    }
  }

  public void addListener(String id, ExecutingListener listener) {
    List<ExecutingListener> listeners = m_listeners.get(id);
    if (listeners == null) {
      listeners = new LinkedList<ExecutingListener>();
    }
    listeners.add(listener);
    m_listeners.put(id, listeners);
  }

  private void processExecutingPages(final Set<String> ids) {
    m_pageExecutingService.getExecutingResultsBatch(ids, new AsyncCallback<List<PageExecutingResult>>() {
      @Override
      public void onFailure(Throwable throwable) {

      }

      @Override
      public void onSuccess(List<PageExecutingResult> results) {
        synchronized (m_resultsToListen) {
          m_resultsToListen.clear();
          for (PageExecutingResult result : results) {
            String id = result.getId();
            List<ExecutingListener> listeners = m_listeners.get(id);
            for (ExecutingListener listener : listeners) {
              listener.processExecutingResult(result);
              if (result.getState() == ExecutingResult.ExecutingState.FINISH) {
                listener.finish();
              } else {
                m_resultsToListen.add(id);
              }
            }
            if (result.getState() == ExecutingResult.ExecutingState.FINISH) {
              for (ExecutingListener listener : listeners) {
                listener.finish();
              }
            }
          }
          if (m_resultsToListen.size() == 0) {
            m_timer.cancel();
          }
        }
      }
    });
  }
}
